/*
 _       __      _ __
| |     / /___ _(_) /____
| | /| / / __ `/ / / ___/
| |/ |/ / /_/ / / (__  )
|__/|__/\__,_/_/_/____/
The electron alternative for Go
(c) Lea Anthony 2019-present
*/
/* jshint esversion: 6 */

import {Error} from './log';
import {SendMessage} from 'ipc';

// Defines a single listener with a maximum number of times to callback

/**
 * The Listener class defines a listener! :-)
 *
 * @class Listener
 */
class Listener {
	/**
	 * Creates an instance of Listener.
	 * @param {function} callback
	 * @param {number} maxCallbacks
	 * @memberof Listener
	 */
	constructor(callback, maxCallbacks) {
		// Default of -1 means infinite
		maxCallbacks = maxCallbacks || -1;
		// Callback invokes the callback with the given data
		// Returns true if this listener should be destroyed
		this.Callback = (data) => {
			callback.apply(null, data);
			// If maxCallbacks is infinite, return false (do not destroy)
			if (maxCallbacks === -1) {
				return false;
			}
			// Decrement maxCallbacks. Return true if now 0, otherwise false
			maxCallbacks -= 1;
			return maxCallbacks === 0;
		};
	}
}

let eventListeners = {};

/**
 * Registers an event listener that will be invoked `maxCallbacks` times before being destroyed
 *
 * @export
 * @param {string} eventName
 * @param {function} callback
 * @param {number} maxCallbacks
 */
export function OnMultiple(eventName, callback, maxCallbacks) {
	eventListeners[eventName] = eventListeners[eventName] || [];
	const thisListener = new Listener(callback, maxCallbacks);
	console.log('Pushing event listener: ' + eventName);
	eventListeners[eventName].push(thisListener);
}

/**
 * Registers an event listener that will be invoked every time the event is emitted
 *
 * @export
 * @param {string} eventName
 * @param {function} callback
 */
export function On(eventName, callback) {
	OnMultiple(eventName, callback);
}

/**
 * Registers listeners for when the system theme changes from light/dark. A bool is
 * sent to the listener, true if it is dark mode.
 *
 * @export
 * @param {function} callback
 */
export function OnThemeChange(callback) {
	On('wails:system:themechange', callback);
}

/**
 * Registers an event listener that will be invoked once then destroyed
 *
 * @export
 * @param {string} eventName
 * @param {function} callback
 */
export function Once(eventName, callback) {
	OnMultiple(eventName, callback, 1);
}

function notifyListeners(eventData) {

	// Get the event name
	let eventName = eventData.name;

	// Check if we have any listeners for this event
	if (eventListeners[eventName]) {

		// Keep a list of listener indexes to destroy
		const newEventListenerList = eventListeners[eventName].slice();

		// Iterate listeners
		for (let count = 0; count < eventListeners[eventName].length; count += 1) {

			// Get next listener
			const listener = eventListeners[eventName][count];

			let data = eventData.data;

			// Do the callback
			const destroy = listener.Callback(data);
			if (destroy) {
				// if the listener indicated to destroy itself, add it to the destroy list
				newEventListenerList.splice(count, 1);
			}
		}

		// Update callbacks with new list of listeners
		eventListeners[eventName] = newEventListenerList;
	}
}

/**
 * Notify informs frontend listeners that an event was emitted with the given data
 *
 * @export
 * @param {string} notifyMessage - encoded notification message

 */
export function Notify(notifyMessage) {

	// Parse the message
	var message;
	try {
		message = JSON.parse(notifyMessage);
	} catch (e) {
		const error = 'Invalid JSON passed to Notify: ' + notifyMessage;
		throw new Error(error);
	}

	notifyListeners(message);
}

/**
 * Emit an event with the given name and data
 *
 * @export
 * @param {string} eventName
 */
export function Emit(eventName) {

	const payload = {
		name: eventName,
		data: [].slice.apply(arguments).slice(1),
	};

	// Notify JS listeners
	notifyListeners(payload);

	// Notify Go listeners
	SendMessage('Ej' + JSON.stringify(payload));

}